package com.easylinkin.linkappapi.gzmmonitor.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easylinkin.linkappapi.alarm.mapper.AlarmMapper;
import com.easylinkin.linkappapi.common.exceptions.BusinessException;
import com.easylinkin.linkappapi.common.service.CommonService;
import com.easylinkin.linkappapi.config.entity.Config;
import com.easylinkin.linkappapi.config.service.ConfigService;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.mapper.DeviceMapper;
import com.easylinkin.linkappapi.deviceunit.entity.DeviceUnit;
import com.easylinkin.linkappapi.deviceunit.service.DeviceUnitService;
import com.easylinkin.linkappapi.gzmmonitor.entity.GzmMonitorItemConfig;
import com.easylinkin.linkappapi.gzmmonitor.mapper.GzmMonitorItemConfigMapper;
import com.easylinkin.linkappapi.gzmmonitor.service.GzmMonitorItemConfigService;
import com.easylinkin.linkappapi.gzmmonitor.vo.GzmAlarmDayQueryVO;
import com.easylinkin.linkappapi.gzmmonitor.vo.GzmAlarmDayVO;
import com.easylinkin.linkappapi.gzmmonitor.vo.GzmAlarmVO;
import com.easylinkin.linkappapi.gzmmonitor.vo.GzmMonitorDeviceUnitVO;
import com.easylinkin.linkappapi.gzmmonitor.vo.GzmMonitorItemConfigVO;
import com.easylinkin.linkappapi.gzmmonitor.vo.GzmMonitorOverviewVO;
import com.easylinkin.linkappapi.ruleengine.entity.RuleEngine;
import com.easylinkin.linkappapi.ruleengine.mapper.RuleEngineMapper;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.tenant.sevice.LinkappTenantService;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

/**
 * <p>
 * 高支模监测点位表 服务实现类
 * </p>
 *
 * @author xiaoyang
 * @since 2023-11-13
 */
@Service
public class GzmMonitorItemConfigServiceImpl extends ServiceImpl<GzmMonitorItemConfigMapper, GzmMonitorItemConfig> implements
    GzmMonitorItemConfigService {

  /**
   * 高支模设备型号key（包含设备属性）
   * [
   *     {
   *         "code": "HH-GZM-QX-0001",
   *         "showAttrs":
   *         [
   *             "f1",
   *             "f2",
   *             "f3"
   *         ]
   *     },
   *     {
   *         "code": "HH-GZM-YL-0002",
   *         "showAttrs":
   *         [
   *             "f1"
   *         ]
   *     },
   *     {
   *         "code": "HH-GZM-CZ-0003",
   *         "showAttrs":
   *         [
   *             "f1"
   *         ]
   *     },
   *     {
   *         "code": "HH-GZM-SP-0004",
   *         "showAttrs":
   *         [
   *             "f1"
   *         ]
   *     }
   * ]
   */
  private static final String GZM_DEVICE_TYPE_UNITS_KEY = "GZM_DEVICE_UNIT_CODES";

  private static final String GZM_DEVICE_ALARM_PREFIXNAME = "高支模-";

  @Resource
  private CommonService commonService;

  @Resource
  private LinkappUserContextProducer linkappUserContextProducer;

  @Resource
  private DeviceUnitService deviceUnitService;

  @Resource
  private DeviceMapper deviceMapper;

  @Resource
  private ConfigService configService;

  @Resource
  private AlarmMapper alarmMapper;

  @Resource
  private RuleEngineMapper ruleEngineMapper;

  @Resource
  private LinkappTenantService linkappTenantService;


  /**
   * 获取高支模设备型号列表(包含设备列表)
   *
   * @return
   */
  @Override
  public List<GzmMonitorDeviceUnitVO> getDeviceListGroupByDeviceType() {
    List<GzmMonitorDeviceUnitVO> result = getGzmDeviceUnitList();

    Device device = new Device();
    device.setLinkthingDelete(1);
    device.setDeleteState(1);
    if (CollectionUtil.isEmpty(result)) {
      return null;
    }
    List<String> unitList = result.stream().map(GzmMonitorDeviceUnitVO::getDeviceUnitCode)
        .collect(Collectors.toList());
    device.setDeviceUnitCodeList(unitList);
    List<Device> devices = deviceMapper.selectDevices(device);
    Map<String, List<Device>> map = null;
    if (CollectionUtil.isNotEmpty(devices)) {
      map = devices.stream().collect(Collectors.groupingBy(Device::getDeviceUnitCode));
    }
    for (GzmMonitorDeviceUnitVO unit : result) {
      if (null != map) {
        List<Device> deviceList = map.get(unit.getDeviceUnitCode());
        unit.setDeviceList(deviceList);
      }
    }
    return result;
  }

  /**
   * 获取高支模设备型号列表
   * @return
   */
  private List<GzmMonitorDeviceUnitVO> getGzmDeviceUnitList(){
    List<GzmMonitorDeviceUnitVO> result = CollectionUtil.newArrayList();
    String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
    Config config = configService.getOneByKeyAndTenantId(
        GZM_DEVICE_TYPE_UNITS_KEY, tenantId);
    if (null == config) {
      config = configService.getOneByKeyAndTenantId(
          GZM_DEVICE_TYPE_UNITS_KEY, null);
    }
    if (null == config) {
      return null;
    }
    String configVal = config.getValue();
    JSONArray jsonArray = JSON.parseArray(configVal);
    if (CollectionUtil.isEmpty(jsonArray)) {
      return null;
    }
    List<DeviceUnit> deviceUnitsList = linkappTenantService.selectDeviceUnitByTenant(tenantId);
    if(CollectionUtil.isEmpty(deviceUnitsList)){
      return null;
    }
    Map<String,DeviceUnit> map = deviceUnitsList.stream().collect(Collectors.toMap(DeviceUnit::getCode,p->p));
    jsonArray.stream().forEach(p -> {
      JSONObject jsonObject = (JSONObject) p;
      if (null != jsonObject && jsonObject.containsKey("code")) {
        String code = jsonObject.getString("code");
        GzmMonitorDeviceUnitVO vo = new GzmMonitorDeviceUnitVO();
        vo.setDeviceUnitCode(code);
        DeviceUnit deviceUnit = map.get(code);
        if (null != deviceUnit) {
          vo.setDeviceUnitName(deviceUnit.getName());
          if (jsonObject.containsKey("showAttrs")) {
            List<String> showAttrs = jsonObject.getJSONArray("showAttrs").toJavaList(String.class);
            vo.setShowAttrs(showAttrs);
          }
          result.add(vo);
        }
      }
    });
    return result;
  }

  @Override
  public List<GzmMonitorItemConfigVO> selectAll() {
    String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
    List<GzmMonitorItemConfigVO> list = baseMapper.selectAllByTenantId(tenantId);
    return list;
  }

  @Override
  public boolean saveAll(List<GzmMonitorItemConfig> items) {
    String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
    LambdaQueryWrapper<GzmMonitorItemConfig> qw = new LambdaQueryWrapper<>();
    qw.eq(GzmMonitorItemConfig::getTenantId, tenantId);
    baseMapper.delete(qw);
    for (GzmMonitorItemConfig itemConfig : items) {
      commonService.setCreateAndModifyInfo(itemConfig);
      itemConfig.setTenantId(tenantId);
      itemConfig.setDeleteState(1);
      validParamRequired(itemConfig);
      validRepeat(itemConfig);
      validParamFormat(itemConfig);
      save(itemConfig);
    }
    return true;
  }

  /**
   * 校验参数必填
   */
  private void validParamRequired(GzmMonitorItemConfig itemConfig) {
    Assert.notNull(itemConfig, "参数为空");
    Assert.notNull(itemConfig.getXaxis(), "坐标x 参数为空");
    Assert.notNull(itemConfig.getYaxis(), "坐标y 参数为空");
    Assert.isTrue(StringUtils.isNotBlank(itemConfig.getDeviceCode()), "设备编号为空");
  }

  /**
   * 校验参数格式
   */
  private void validParamFormat(GzmMonitorItemConfig itemConfig) {
    Assert.isTrue(itemConfig.getDeviceCode() == null
            || itemConfig.getDeviceCode().length() <= 32,
        "名称超长");
  }

  /**
   * 校验重复
   */
  private void validRepeat(GzmMonitorItemConfig itemConfig) {
    String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
    LambdaQueryWrapper<GzmMonitorItemConfig> qw = new LambdaQueryWrapper<>();
    qw.eq(GzmMonitorItemConfig::getTenantId, tenantId)
        .eq(GzmMonitorItemConfig::getDeleteState,1)
        .eq(GzmMonitorItemConfig::getDeviceCode, itemConfig.getDeviceCode());
    List<GzmMonitorItemConfig> list = baseMapper.selectList(qw);
    if (list.size() > 0) {
      throw new BusinessException("设备编号有重复");
    }
  }

  @Override
  public GzmMonitorOverviewVO getGzmMonitorOverview() {
    GzmMonitorOverviewVO overviewVO = new GzmMonitorOverviewVO();
    Integer totalDeviceCount = 0;
    Integer totalAlarmCount = 0;
    // 1.获取高支模设备型号对应告警数量和设备数
    Date startTime = DateUtil.beginOfDay(new Date());// 今日开始
    Date endTime = new Date();
    List<GzmMonitorDeviceUnitVO> result = getGzmAlarmGroupByMonitorDeviceUnit(startTime,endTime);
    // 2.今日统计设备数和告警数
    if (CollectionUtil.isNotEmpty(result)) {
      for (GzmMonitorDeviceUnitVO unit : result) {
        totalDeviceCount += unit.getDeviceCount();
        totalAlarmCount += unit.getAlarmCount();
      }
      // 按照设备数量排序
      result = result.stream().sorted((p1,p2)->p2.getDeviceCount().compareTo(p1.getDeviceCount())).collect(
          Collectors.toList());
    }
    overviewVO.setDeviceCount(totalDeviceCount);
    overviewVO.setAlarmCount(totalAlarmCount);
    overviewVO.setGzmDeviceUnitList(result);
    return overviewVO;
  }

  @Override
  public List<GzmAlarmVO> getGzmAlarmTypeStatistics() {
    List<GzmAlarmVO> result = new ArrayList<>();
    // 1.查询告警类型对应告警统计数据
    String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
    List<RuleEngine> ruleEngineList = getGzmRuleEngineList(tenantId);
    if (CollectionUtil.isEmpty(ruleEngineList)) {
      return null;
    }
    List<String> ruleEngineIds = ruleEngineList.stream().map(RuleEngine::getId)
        .collect(Collectors.toList());
    List<GzmAlarmVO> gzmAlarmTypeStatistics = alarmMapper.getGzmAlarmTypeStatistics(ruleEngineIds,tenantId);
    Map<String,GzmAlarmVO> map = null;
    if(CollectionUtil.isNotEmpty(gzmAlarmTypeStatistics)){
      map = gzmAlarmTypeStatistics.stream().collect(Collectors.toMap(GzmAlarmVO::getRuleEngineId,p->p));
    }
    // 2.补全数据
    for (RuleEngine ruleEngine : ruleEngineList) {
      GzmAlarmVO gzmAlarmVO = new GzmAlarmVO();
      gzmAlarmVO.setRuleEngineId(ruleEngine.getId());
      gzmAlarmVO.setRuleEngineName(deletePrefixName(ruleEngine.getName()));
      gzmAlarmVO.setAlarmCount(0);
      if(null != map && map.containsKey(ruleEngine.getId())){
        gzmAlarmVO.setAlarmCount(map.get(ruleEngine.getId()).getAlarmCount());
      }
      result.add(gzmAlarmVO);
    }
    return result;
  }

  @Override
  public List<GzmAlarmVO> alarmTrend(GzmAlarmDayQueryVO queryVO) {
    List<GzmAlarmVO> result = new ArrayList<>();
    // 1.校验请求参数
    Assert.notNull(queryVO.getStartTime(), "开始时间不能为空");
    Assert.notNull(queryVO.getEndTime(), "结束时间不能为空");
    // 判断时间大小
    if (queryVO.getStartTime().getTime() > queryVO.getEndTime().getTime()) {
      throw new BusinessException("开始时间不能大于结束时间");
    }
    // 2.根据类型分类统计告警数量
    String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
    queryVO.setTenantId(tenantId);
    List<RuleEngine> ruleEngineList = getGzmRuleEngineList(tenantId);
    if(CollectionUtil.isEmpty(ruleEngineList)){
      return null;
    }
    // 3.循环查询没给告警规则的告警趋势
    ruleEngineList.stream().forEach(p->result.add(queryAlarmTrendByTime(p,queryVO)));
    return result;
  }

  /**
   * 查询告警趋势
   *
   * @param ruleEngine
   * @param queryVO
   * @return
   */
  private GzmAlarmVO queryAlarmTrendByTime(RuleEngine ruleEngine,GzmAlarmDayQueryVO queryVO){
    // 1.初始化高支模监测类型告警统计对象
    GzmAlarmVO gzmAlarmVO = new GzmAlarmVO();
    String ruleEngineId = ruleEngine.getId();
    gzmAlarmVO.setRuleEngineId(ruleEngineId);
    gzmAlarmVO.setRuleEngineName(deletePrefixName(ruleEngine.getName()));
    // 2.查询该监测类型告警数量
    List<GzmAlarmDayVO> alarmTrendByTime = alarmMapper.getAlarmTrendByTime(queryVO.getTenantId(),
        ruleEngineId, queryVO.getStartTime(), queryVO.getEndTime());
    Map<String,GzmAlarmDayVO> map = null;
    if(CollectionUtil.isNotEmpty(alarmTrendByTime)){
      map = alarmTrendByTime.stream().collect(Collectors.toMap(GzmAlarmDayVO::getDate,p->p));
    }
    List<DateTime> dateTimes = DateUtil.rangeToList(queryVO.getStartTime(), queryVO.getEndTime(), DateField.DAY_OF_YEAR);
    List<String> dateStrList = dateTimes.stream().map(p->DateUtil.format(p,"yyyy-MM-dd")).collect(Collectors.toList());
    List<GzmAlarmDayVO> gzmAlarmDayVOList = new ArrayList<>();
    // 3.补全时间段内的数据
    for (String p : dateStrList) {
      GzmAlarmDayVO gzmAlarmDayVO = new GzmAlarmDayVO();
      gzmAlarmDayVO.setDate(p);
      gzmAlarmDayVO.setCount(0);
      if (null != map && map.containsKey(p)) {
        gzmAlarmDayVO.setCount(map.get(p).getCount());
      }
      gzmAlarmDayVOList.add(gzmAlarmDayVO);
    }
    gzmAlarmVO.setAlarmDayList(gzmAlarmDayVOList);
    return gzmAlarmVO;
  }

  // 去掉告警规则名称前缀 “高支模-”
  private String deletePrefixName(String name){
    if(StringUtils.isNotBlank(name) && name.startsWith(GZM_DEVICE_ALARM_PREFIXNAME)){
      name = name.substring(GZM_DEVICE_ALARM_PREFIXNAME.length());
    }
    return name;
  }

  /**
   * 获取租户下所有高支模告警规则
   *
   * @param tenantId
   * @return
   */
  private List<RuleEngine> getGzmRuleEngineList(String tenantId){
    QueryWrapper<RuleEngine> reqw = new QueryWrapper<>();
    reqw.eq("tenant_id", tenantId);
    reqw.likeRight(StringUtils.isNotBlank(GZM_DEVICE_ALARM_PREFIXNAME), "name", GZM_DEVICE_ALARM_PREFIXNAME);
    List<RuleEngine> ruleEngineList = ruleEngineMapper.selectList(reqw);
    return ruleEngineList;
  }

  /**
   * 获取高支模监测类型对应告警统计
   * @param startTime
   * @param endTime
   * @return
   */
  private List<GzmMonitorDeviceUnitVO> getGzmAlarmGroupByMonitorDeviceUnit(Date startTime, Date endTime){
    // 1.获取高支模设备型号列表(包含设备列表)
    List<GzmMonitorDeviceUnitVO> result = getDeviceListGroupByDeviceType();
    if (CollectionUtil.isEmpty(result)) {
      return null;
    }
    String tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
    // 2.获取租户下所有高支模告警规则
    List<RuleEngine> ruleEngineList = getGzmRuleEngineList(tenantId);
    // 3.获取高支模设备型号对应告警数量
    result.forEach(p -> {
      Integer alarmCount = 0;
      Integer deviceCount = 0;
      if(CollectionUtil.isNotEmpty(p.getDeviceList())){
        List<String> deviceCodes = p.getDeviceList().stream().map(Device::getCode)
            .collect(Collectors.toList());
        deviceCount = p.getDeviceList().size();
        if (CollectionUtil.isNotEmpty(ruleEngineList)) {
          List<String> ruleEngineIds = ruleEngineList.stream().map(RuleEngine::getId)
              .collect(Collectors.toList());
          // 4.根据设备code和规则引擎 查询告警数
          alarmCount = alarmMapper.countAlarmByDeviceCodesGlobal(deviceCodes, ruleEngineIds,startTime,endTime,tenantId);
        }
      }
      p.setAlarmCount(alarmCount);
      p.setDeviceCount(deviceCount);
      p.setDeviceList(null);// 清空设备列表
    });
    return result;
  }
}
